package org.testis.testdriver;

import org.testis.testdriver.conditions.ConditionEvaluatable;
import org.testis.testdriver.operations.TestPartOperationContext;
import org.testis.data.TestStepDataObject;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.ListIterator;
import java.util.Map;
import com.mallardsoft.tuple.Pair;
import java.util.Iterator;
import java.util.LinkedHashMap;

public class TestStep extends TestPart implements ConditionEvaluatable {

    /**
     * Holds the action name as string
     */
    private String m_ActionName;
    /**
     * Holds the Mac Address of the TestPC that will execute this TestStep
     */
    private String m_TestPcMacAddr;
    /**
     * Holds the Module that this TestStep will execute  as enumaration
     */
    private AtofEnumaration.AtofModule m_ModuleTypeName;
    /**
     * Holds the list of supported devices. Only used for WebAutomationModule steps.
     */
    private String[] m_SupportedDeviceList;
    /**
     * Holds the list of data that will be applied to the TestStep.
     */
    private LinkedList<TestDataObject> m_TestDataList;
    /**
     * Holds if the Test Step is synchronous or not.
     */
    private boolean m_AsynchCall;
    /**
     * Holds if the list of the Data is iterated on TestStep or Test Case basis.
     */
    private boolean m_DataArrayAppliesToCase;
    /**
     * Holds if the delay time in seconds.
     */
    private int m_DelayTime;
    /**
     * Holds if the delay time in seconds after execution.
     */
    private int m_DelayTimeAfterExecute;

    /**
     * Holds if the time out in seconds.
     */
    private int m_timeOut;

   
    /**
     * Holds the expected success percentage. Success percentage is evaluated
     * with multiple data applied to TestStep and the success of each data.
     */
    private short m_Step_SuccessPercentage = 100;
    private LinkedHashMap<String, String> m_moduleParams;
    
    
    /*
    * RUN TIME PARAMETERS
    * /
     *
     */
    /**
     * Holds the result set specific to TestStep.
     */
    private Map<Integer, Pair<TestStepDataObject, Boolean>> m_step_resultSet;
    /**
     * Holds the ListIterator that will be used to Iterate over the Data objects.
     */
    private ListIterator m_TestDataIterator;

    /**
     * Constructor used to construct a Test Step
     * 
     * @param testPartId The id of the Test Step.
     * @param testStepName The name of the Test Step.
     * @param partDesc The description of the Test Step.
     *      
     */
    public TestStep(String testPartId,
            String testStepName,
            String partDesc) {

        this.m_testPartId = testPartId;
        this.m_testPartName = testStepName;
        this.m_partDescription = partDesc;
        this.m_containerType = AtofEnumaration.TestPartType.TestStep;

        m_TestDataList = new LinkedList<TestDataObject>();

        m_step_resultSet =
                new HashMap<Integer, Pair<TestStepDataObject, Boolean>>();
        m_runOperations =
                new String[]{"TestPartOperationExecuteTestStep",
                    "TestPartOperationSetThreeDResult"
                };

        m_moduleParams = new LinkedHashMap<String, String>();
        m_retryCount = 0;

    }

    /**
     * This method is used to drive the TestStep.
     * 
     * @param  mode  The Debugging mode of the StepContainer execution.
     * @return boolean result of the  TestPart execution
     *      
     */
    public boolean run(AtofEnumaration.DebuggingMode mode) throws
            InterruptedException {

        TestPartOperationContext currentContext = new TestPartOperationContext(
                this);
        return currentContext.runContext(mode);


    }

    /**
     * You cannot add a TestPart to a Test Step
     * 
     *      
     */
    public void add(TestPart c) {
        // You cannot add a TestPart to a Test Step
    }

    /**
     * You cannot remove a Test Step form Test Part
     * 
     *      
     */
    public void remove(TestPart c) {
        // You cannot remove a Test Step form Test Part
    }

    /**
     * This method is used to set Test Step parameters
     * @param testPcName The name of the Test PC (ATOF must be installed on that PC)
     * @param moduleName The name of the module of ATOF
     * @param actName The name of the action that will be taken
     * @param supportedDevices The list of the supported devices for this Test Step.    
     */
    public void setTestStepParams(String testPcMacAddress,
            AtofEnumaration.AtofModule moduleName,
            String actName,
            String[] supportedDevices) {
        this.m_TestPcMacAddr = testPcMacAddress;
        this.m_ModuleTypeName = moduleName;
        this.m_ActionName = actName;
        this.m_SupportedDeviceList = supportedDevices;
    }

    /**
     * This method is used to set Test Step runtime parameters
     * @param timeOut The timout value in seconds
     * @param delayTime The delaying time in seconds.
     * @param noOfRetryOnFail The count of retries if the Test Step fails.
     * @param isAsynch The flag indicating wheter the Test Step executed asynchronously.
     * @param dataArrayAppliesToCase The flag indicating if the DataObject Array elements
     * applies to TestCase. If this flag is set to true the Test Step will iterate through
     * its DataObjects by letting its parent Test Case iterating through its Test Steps
     * each time. 
     * @param isCritical The flag indicating wheter the TestStep is critical or not
     */
    public void setTestStepRunTime(int timeOut,
            int delayTime,
            int delayTimeAfterExecute,
            int noOfRetryOnFail,
            boolean isAsynch,
            boolean dataArrayAppliesToCase,
            boolean isCritical) {
        this.m_timeOut = timeOut;
        this.m_DelayTime = delayTime;
        this.m_retryOnFailCount = noOfRetryOnFail;
        this.m_AsynchCall = isAsynch;
        this.m_DataArrayAppliesToCase = dataArrayAppliesToCase;
        this.m_isCritical = isCritical;
        this.m_DelayTimeAfterExecute =delayTimeAfterExecute;
    }

    /**
     * This method is used to add TestDataObject to the list of TestDataObjects hold
     * in the TestStep.
     * @param val The concerete TestDataObject object.
     */
    public void addTestStepData(TestDataObject val) {
        this.m_TestDataList.add(val);

    }

    public void setModuleParams(LinkedHashMap<String, String> toSet) {
        this.m_moduleParams = toSet;
    }

    public LinkedHashMap<String, String> getModuleParams() {
        return m_moduleParams;
    }

    public TestDataObject getTestStepDataByIndex(int index) {
        return m_TestDataList.get(index);

    }

    /**
     * This method is will always return a null Iterator as TestStep has no child 
     * elements.
     */
    public TestIterator createIter() {
        return null;
    }

    public int getTimeOut() {
        return m_timeOut;
    }

    public boolean getAsynchCall() {
        return m_AsynchCall;
    }

    public void setAsynchCall(boolean val) {
        this.m_AsynchCall = val;
    }

    public boolean getDataArrayAppliesToCase() {
        return m_DataArrayAppliesToCase;
    }

    public void setDataArrayAppliesToCase(boolean val) {
        this.m_DataArrayAppliesToCase = val;
    }

    public String getTestPcMacAddr() {
        return m_TestPcMacAddr;
    }

    public AtofEnumaration.AtofModule getModuleTypeName() {
        return m_ModuleTypeName;
    }

    public String[] getSupportedDeviceList() {
        return m_SupportedDeviceList;
    }

    /**
     * This method is used to check if there is more DataObjects in the list.
     * If there are no more TestDataObjects the iterator is reseted to null.     
     */
    public boolean hasNextDataObject() {
        boolean retVal = this.m_TestDataIterator.hasNext();
        if (retVal == false) {
            this.m_TestDataIterator = null;
        }


        return retVal;
    }

    public String getActionName() {
        return m_ActionName;
    }

    public int getDelayTime() {
        return m_DelayTime;
    }
    public void setDelayTime(int delaytime){
        m_DelayTime = delaytime;
    }

    public short getM_Step_SuccessPercentage() {
        return m_Step_SuccessPercentage;
    }

    public void setM_Step_SuccessPercentage(short m_Step_SuccessPercentage) {
        this.m_Step_SuccessPercentage = m_Step_SuccessPercentage;
    }

    public ListIterator getTestDataIterator() {
        return m_TestDataIterator;
    }

    public void setTestDataIterator(ListIterator testDataIterator) {
        this.m_TestDataIterator = testDataIterator;
    }

    public Map<Integer, Pair<TestStepDataObject, Boolean>> getStepResultSet() {
        return m_step_resultSet;
    }

    public LinkedList<TestDataObject> getTestDataList() {
        return m_TestDataList;
    }

    public String getPartNameLongFromChild() {

        return this.getPartName();// The name can be same as part Name because
        // if we add Test Case Invocation Count and Data Count information to the
        // name there will be too much confusion in the logs. Therefore I only
        // add that kind of information in partGuidLong for uniqueness of logs
        // for different invocations and data counts.

    }
    public int getDelayTimeAfterExecute() {
        return m_DelayTimeAfterExecute;
    }

    public void setDelayTimeAfterExecute(int delayTimeAfterExecute) {
        this.m_DelayTimeAfterExecute = delayTimeAfterExecute;
    }


    public String getPartNameLongFromContainer() {

        return getPartNameLongFromChild();// The name can be same as part Name because
        // if we add Test Case Invocation Count and Data Count information to the
        // name there will be too much confusion in the logs. Therefore I only
        // add that kind of information in partGuidLong for uniqueness of logs
        // for different invocations and data counts.


    }

    public String getPartGuidLongFromChild() {
        Integer testCaseDataIndex = 0;

        testCaseDataIndex = ((StepContainer) this.getParent()).getTestCaseDataIndex() + 1;
        String testStepGuid = m_partGuid + ((StepContainer) this.getParent()).getInvocationCountIndex().toString()
                + testCaseDataIndex.toString();
        return testStepGuid;
    }

    public String getPartGuidLongFromContainer() {

        return getPartGuidLongFromChild();
    }

    public String getTsgSpecificRetryCount() {
        String tsgRetryCount = ""; // Test Step Group specific retry count info.

        StepContainer parentContainer = (StepContainer) this.getParent();
        if (parentContainer.getContainerType()
                == AtofEnumaration.TestPartType.TestStepGroup) {
            tsgRetryCount = " [" + TestStringConstants.getTestStepGroupRetryCountString()
                    + String.valueOf(parentContainer.getRetryCount()) + "]";
        }

        return tsgRetryCount;


    }

    @Override
    public void resetTestPartRunTimeParameters() {
        this.setTestPartResult(true);
        m_step_resultSet.clear();
        m_case_resultSet.clear();
        m_failedconditions.clear();
        m_TestDataIterator = null;

        for (Iterator<TestDataObject> it = this.getTestDataList().iterator(); it.hasNext();) {
            it.next().getFailedConditions().clear();
        }
    }

    @Override
    public int hashCode() {
        int hash = 3;
        hash =
                11 * hash
                + (this.m_ActionName != null ? this.m_ActionName.hashCode() : 0);
        hash =
                11 * hash
                + (this.m_TestPcMacAddr != null ? this.m_TestPcMacAddr.hashCode()
                : 0);
        hash =
                11 * hash
                + (this.m_ModuleTypeName != null ? this.m_ModuleTypeName.hashCode()
                : 0);
        hash =
                11 * hash
                + (this.m_SupportedDeviceList != null
                ? this.m_SupportedDeviceList.hashCode() : 0);
        if (this.m_TestDataList != null) {
            ListIterator ourIt = m_TestDataList.listIterator();
            while (ourIt.hasNext()) {
                TestDataObject ourObj = (TestDataObject) ourIt.next();
                hash = 11 * hash + ourObj.hashCode();
            }
        }

        hash =
                11 * hash
                + (this.m_TestDataIterator != null ? this.m_TestDataIterator.hashCode()
                : 0);
        hash = 11 * hash + (this.m_AsynchCall ? 1 : 0);
        hash = 11 * hash + (this.m_DataArrayAppliesToCase ? 1 : 0);
        hash = 11 * hash + this.m_DelayTime;
        hash = 11 * hash + this.m_timeOut;
        hash =
                11 * hash
                + (this.m_step_resultSet != null ? this.m_step_resultSet.hashCode()
                : 0);
        hash = 11 * hash + this.m_Step_SuccessPercentage;
        hash = 79 * hash + super.hashCode();
        return hash;
    }

    @Override
    public boolean equals(Object obj) {
        if (obj instanceof TestStep) {
            return ((TestStep) obj).hashCode() == this.hashCode();
        } else {
            return false;
        }

    }

}

